using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Data.SqlServerCe;

namespace FeedFly
{
    /// <summary>
    /// Main navigation form. 
    /// </summary>
    public partial class MainForm : Form
    {
        private Queue<NotificationMessage> notificationQueue = new Queue<NotificationMessage>(4);
        private Stack<SelectedFolder> folderStack = new Stack<SelectedFolder>(4);

        private const string UR_PREFIX = "(";
        private const string UR_SUFFIX = ") ";
        private const string UR_NONE = "0";
        private const string MARK_READ = "Mark Read";
        private const string MARK_UNREAD = "Mark Unread";
        private const string DOWNLOAD_STOP = "Stop Downloads";
        private const string DOWNLOAD_RESUME = "Resume Downloads";

        // create delegates for updating UI from background thread
        private delegate void AdjustUnreadCountDelegate(UnreadCountAdjustment uca);
        private delegate void RefreshFolderDelegate(int folderId);
        private delegate void StopDownloadingDelegate(bool stop);
        private delegate void QueueNotificationDelegate(NotificationMessage msg);

        public MainForm()
        {
            InitializeComponent();
        }

        public string GetBreadCrumbs()
        {
            return folderStack.Peek().BreadCrumbs;
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            // load root folder by default
            SelectedFolder sf = new SelectedFolder(1, "Home", 0);
            sf.BreadCrumbs = sf.Name;
            LoadFolder(sf);
            feedListView.Focus();
        }

        #region Public Interface
        public void RefreshFolder(int folderId)
        {
            if (InvokeRequired)
            {
                BeginInvoke((RefreshFolderDelegate)RefreshFolder, new object[] { folderId });
                return;
            }

            // if requested folder is active, refresh
            SelectedFolder currentFolder = folderStack.Peek();

            if (currentFolder.Id == folderId)
                this.LoadFolder(currentFolder);
        }

        public void UpdateFeedTitle(FeedInterface feed)
        {
            // only attempt to update if feed is in current list
            if (feed.FolderId == folderStack.Peek().Id)
            {
                ListViewItem item;
                String idString = feed.Id.ToString();
                StringBuilder formattedTitle = new StringBuilder(100);

                // loop through listview items collection to find item
                for (int i = 0; i < feedListView.Items.Count; i++)
                {
                    item = feedListView.Items[i];

                    if (item.ImageIndex == 1 && item.SubItems[1].Text == idString)
                    {
                        item.SubItems[2].Text = feed.Title;
                        item.SubItems[0].Text = BuildFormattedTitle(feed.Title, 
                            Convert.ToInt32(item.SubItems[3].Text), formattedTitle);
                    }
                }
            }
        }

        public void QueueNotification(NotificationMessage msg)
        {
            if (InvokeRequired)
            {
                BeginInvoke((QueueNotificationDelegate)QueueNotification, new object[] { msg });
                return;
            }

            notificationQueue.Enqueue(msg);

            // show the notification if not currently visible.
            if (!MessageNotification.Visible)
                ShowNotification();
        }

        private void ShowNotification()
        {
            NotificationMessage notification = notificationQueue.Peek();
            MessageNotification.Caption = notification.Caption;
            MessageNotification.Text = notification.Message;
            MessageNotification.Visible = true;
        }

        public void StopDownloading(bool stop)
        {
            if (InvokeRequired)
            {
                BeginInvoke((StopDownloadingDelegate)StopDownloading, new object[] { stop });
                return;
            }

            // update menu item text based on request flag
            DownloadPollingMenuItem.Text = (stop) ? DOWNLOAD_RESUME : DOWNLOAD_STOP;
        }

        public void AdjustUnreadCount(UnreadCountAdjustment uca)
        {
            if (InvokeRequired)
            {
                BeginInvoke((AdjustUnreadCountDelegate)AdjustUnreadCount, new object[] { uca });
                return;
            }

            // update only if parent folder is visible
            if (folderStack.Peek().Id == uca.FolderId)
            {
                ListViewItem item;
                String idString = uca.Id.ToString();
                StringBuilder formattedTitle = new StringBuilder(100);
                int currentCount;

                // loop through listview items collection to find item
                for (int i = 0; i < feedListView.Items.Count; i++)
                {
                    item = feedListView.Items[i];

                    if (item.SubItems[1].Text == idString &&
                         ((item.ImageIndex == 0 && uca.IsFolder)
                            || (item.ImageIndex == 1 && !uca.IsFolder)))
                    {
                        currentCount = Convert.ToInt32(item.SubItems[3].Text);
                        currentCount += uca.UnreadDelta;

                        item.SubItems[0].Text = BuildFormattedTitle(item.SubItems[2].Text, currentCount, formattedTitle);
                        item.SubItems[3].Text = currentCount.ToString();

                        // toggle text of mark read menu item if it's selected
                        if (item.Focused)
                            this.markReadMenuItem.Text = (currentCount > 0) ? MARK_READ : MARK_UNREAD;
                    }
                }
            }
        }
        #endregion

        private void MessageNotification_ResponseSubmitted(object sender, Microsoft.WindowsCE.Forms.ResponseSubmittedEventArgs e)
        {
            // only remove from queue after responsed to by user
            NotificationMessage msg = notificationQueue.Dequeue();

            // see if log to file option was chosen
            if (e.Response == "details")
                Logging.WriteNotification(msg);

            if (notificationQueue.Count > 0)
                ShowNotification();
            else
                // no more messages in queue, so hide notification
                MessageNotification.Visible = false;
        }

        private void LoadFolder(SelectedFolder f)
        {
            // change cursor to WaitCursor
            Cursor.Current = Cursors.WaitCursor;

            // remove existing items
            if(feedListView.Items.Count > 0)
                feedListView.Items.Clear();
            
            feedListView.BeginUpdate();

            SqlCeConnection conn = Data.GetConnection();

            // load folders first, then feeds
            loadFolders(Folder.GetFolderDataReader(f.Id, conn));
            loadFeeds(Feed.GetFeedDataReader(f.Id, conn));

            conn.Close();
            conn.Dispose();

            // add new folder to stack if not already there
            if (folderStack.Count == 0)
                folderStack.Push(f);
            else
            {
                if (!folderStack.Contains(f))
                {
                    f.BreadCrumbs = folderStack.Peek().BreadCrumbs + " > " + f.Name;
                    folderStack.Push(f);
                }
            }
            
            // update title
            folderNameLabel.Text = f.Name;

            // restore previous selected folder
            if (feedListView.Items.Count > 0)
            {
                ListViewItem focusItem;

                if (feedListView.Items.Count < f.SelectedIndex + 1)
                    focusItem = feedListView.Items[0];
                else
                    focusItem = feedListView.Items[f.SelectedIndex];

                focusItem.Focused = true;
                focusItem.Selected = true;

                EnableMenuItems(true);
            }
            else
                EnableMenuItems(false);

            feedListView.EndUpdate();

            bool atHome = (folderStack.Count == 1);

            goBackMenuItem.Enabled = !atHome;
            goBackPictureBox.Visible = !atHome;
            LogoPictureBox.Visible = atHome;

            // change cursor back to default
            Cursor.Current = Cursors.Default;
        }

        private void EnableMenuItems(bool enabled)
        {
            markReadMenuItem.Enabled = enabled;
            deleteMenuItem.Enabled = enabled;
            DownloadMenuItem.Enabled = enabled;
            openMenuItem.Enabled = enabled;
            MarkAllMenuItem.Enabled = enabled;
        }

        private void loadFolders(SqlCeDataReader rdr)
        {     
            string[] columns = new string[4];
            StringBuilder formattedTitle = new StringBuilder(100);

            while (rdr.Read())
                feedListView.Items.Add(BuildItem(rdr.GetInt32(0), rdr.GetString(1), rdr.GetInt32(3), formattedTitle, columns, true));

            rdr.Close();
            rdr.Dispose();
        }

        private void loadFeeds(SqlCeDataReader rdr)
        {             
            string[] columns = new string[4];
            StringBuilder formattedTitle = new StringBuilder(100);

            while (rdr.Read())
                feedListView.Items.Add(BuildItem(rdr.GetInt32(0), rdr.GetString(2), (int)rdr.GetInt16(8), formattedTitle, columns, false));
            
            rdr.Close();
            rdr.Dispose();        
        }

        private ListViewItem BuildItem(int id, string title, int unreadCount, StringBuilder formattedTitle, string[] columns, bool isFolder)
        {
            columns[0] = BuildFormattedTitle(title,unreadCount,formattedTitle);
            columns[1] = id.ToString();
            columns[2] = title;
            columns[3] = unreadCount.ToString();

            ListViewItem item = new ListViewItem(columns);
            item.ImageIndex = (isFolder) ? 0 : 1;

            return item;
        }

        private string BuildFormattedTitle(string title, int unreadCount, StringBuilder formattedTitle)
        {
            string result;

            formattedTitle.Length = 0;

            if (unreadCount > 0)
                formattedTitle.Append(UR_PREFIX).Append(unreadCount).Append(UR_SUFFIX);

            result = formattedTitle.Append(title).ToString();
                       
            return result;
        }

        private void feedListView_SelectedIndexChanged(object sender, EventArgs e)
        {
            // selected index changed event fires when items are cleared from listview, so test for empty list 
            if (feedListView.Items.Count > 0 && feedListView.SelectedIndices.Count > 0)
                this.markReadMenuItem.Text = 
                    (feedListView.Items[feedListView.SelectedIndices[0]].SubItems[3].Text == "0") ? MARK_UNREAD : MARK_READ;
        }

        private void feedListView_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)            
            {
                //case Keys.Enter:
                case Keys.Right:   
                case Keys.LButton:
                    viewItem();
                    break;
                case Keys.Left:
                    goBack();
                    break;
            }
        }

        private void markReadMenuItem_Click(object sender, EventArgs e)
        {
            ListViewItem currentItem = feedListView.FocusedItem;

            bool isRead = !(currentItem.SubItems[3].Text == UR_NONE);
            int itemId = Convert.ToInt32(currentItem.SubItems[1].Text);

            // Allow AdjustUnreadCount to update text async instead
            //     This is because if the folder contains multiple children,
            //     intermediate updating will occur and confuse the user.
            //currentItem.Text = currentItem.SubItems[2].Text;

            if (isFolderSelected())
                ThreadPool.QueueUserWorkItem(new WaitCallback(markFolder)
                    , new Folder.MarkRequest(itemId, isRead));
            else
            {
                // if feed being marked is currently visible in the item list
                // mark all items in list as read/unread
                if (Screens.ItemList.GetCurrentFeedId() == itemId)
                    Screens.ItemList.MarkAllItems(isRead);

                ThreadPool.QueueUserWorkItem(new WaitCallback(markFeed)
                    , new Feed.MarkRequest(itemId, folderStack.Peek().Id, isRead));
            }

            // toggle menu item
            this.markReadMenuItem.Text = (isRead) ? MARK_READ : MARK_UNREAD;

        }

        private void MarkAllMenuItem_Click(object sender, EventArgs e)
        {
            // This was commented out since adjustUnreadCount methods come back and update this screen later.
            // loop through item list and change formatted titles
            // for (int i = 0; i < feedListView.Items.Count; i++)
            //    feedListView.Items[i].SubItems[0].Text = feedListView.Items[i].SubItems[2].Text;

            // call async job to mark all as read
            ThreadPool.QueueUserWorkItem(new WaitCallback(markFolder)
                , new Folder.MarkRequest(folderStack.Peek().Id, true));

            // change menu item text 
            this.markReadMenuItem.Text = MARK_UNREAD;
        }

        private void markFolder(object mr)
        {
            Folder.MarkRequest r = (Folder.MarkRequest)mr;
            Folder.Mark(r);
        }

        private void markFeed(object mr)
        {
            Feed.MarkRequest r = (Feed.MarkRequest)mr;
            Feed.Mark(r);
        }

        private void refreshMenuItem_Click(object sender, EventArgs e)
        {
            LoadFolder(folderStack.Peek());
        }

        private void goBack()
        {
            if (folderStack.Count > 1)
            {
                folderStack.Pop();
                LoadFolder(folderStack.Peek());
            }
        }

        private void viewItem()
        {
            if (feedListView.Items.Count > 0)
            {
                ListViewItem currentItem = feedListView.FocusedItem;

                // is current item a folder?
                if (currentItem.ImageIndex == 0)
                {
                    // set current index before loading another folder
                    SelectedFolder f = folderStack.Pop();
                    f.SelectedIndex = currentItem.Index;
                    folderStack.Push(f);
                    LoadFolder(new SelectedFolder(
                        Convert.ToInt32(currentItem.SubItems[1].Text)
                        , currentItem.SubItems[2].Text, 0));
                }
                else
                {
                    Screens.ItemList.LoadFeed(
                        Convert.ToInt32(currentItem.SubItems[1].Text)
                        , folderStack.Peek().Id
                        , currentItem.SubItems[2].Text);
                    Screens.ItemList.Show();
                    Hide();
                }
            }
        }

        private bool isFolderSelected()
        {
            return (feedListView.FocusedItem.ImageIndex == 0);
        }

        private void viewItemEvent(object sender, EventArgs e)
        {
            viewItem();
        }

        private void goBackToParent(object sender, EventArgs e)
        {
            goBack();
        }

        private void goBackPictureBox_MouseDown(object sender, MouseEventArgs e)
        {
            toggleGoBackButton(true);
        }

        private void goBackPictureBox_MouseUp(object sender, MouseEventArgs e)
        {
            toggleGoBackButton(false);

            if (folderStack.Count < 2)
                goBackPictureBox.Visible = false;
        }        
        
        private void toggleGoBackButton(bool selected)
        {
            goBackPictureBox.Visible = !selected;
            goBackPictureBoxSelected.Visible = selected;
        }

        private void addFeedMenuItem_Click(object sender, EventArgs e)
        {
            SelectedFolder sf = folderStack.Peek();
            Screens.AddFeed.PrepareForm(sf.Id, sf.BreadCrumbs);
            Screens.AddFeed.Show();
            Hide();
        }

        private void addFolderMenuItem_Click(object sender, EventArgs e)
        {
            SelectedFolder sf = folderStack.Peek();
            Screens.AddFolder.PrepareForm(sf.Id, sf.BreadCrumbs);
            Screens.AddFolder.Show();
            Hide();
        }

        private void DownloadPollingMenuItem_Click(object sender, EventArgs e)
        {
            if (DownloadPollingMenuItem.Text == DOWNLOAD_STOP)
            {
                DownloadPollingMenuItem.Text = DOWNLOAD_RESUME;
                Net.StopDownloads();
            }
            else
            {
                DownloadPollingMenuItem.Text = DOWNLOAD_STOP;
                Net.ResumeDownloads();
                ThreadPool.QueueUserWorkItem(new WaitCallback(Net.ProcessDownloads));
            }
        }

        private void downloadSelectedMenuItem_Click(object sender, EventArgs e)
        {
            if (isFolderSelected())
                // loop through all subfolders and update feeds within
                ThreadPool.QueueUserWorkItem(new WaitCallback(downloadFolder)
                    , feedListView.FocusedItem.SubItems[1].Text);
            else
                ThreadPool.QueueUserWorkItem(new WaitCallback(downloadFeed)
                    , feedListView.FocusedItem.SubItems[1].Text);
        }

        private void DownloadAllMenuItem_Click(object sender, EventArgs e)
        {
            // enqueue current folder to download queue
            ThreadPool.QueueUserWorkItem(new WaitCallback(downloadFolder)
                , folderStack.Peek().Id.ToString());
        }

        private void downloadFolder(object folderId)
        {
            Folder.DownloadUpdate(folderId);
            Net.ProcessDownloads(null);
        }

        // updated to use the download queue
        // move Feed object created
        private void downloadFeed(object feedId)
        {
            Net.EnqueueDownload(Feed.GetFeed(Convert.ToInt32(feedId)));
            Net.ProcessDownloads(null);
        }

        private void DeleteAllMenuItem_Click(object sender, EventArgs e)
        {
            // kick off async job to delete 
            ThreadPool.QueueUserWorkItem(new WaitCallback(DeleteAll));

            // remove all items from list
            //feedListView.Items.Clear();

            // disable menu controls for items
            //EnableMenuItems(false);
        }

        private void DeleteAll(object o)
        {
           Folder.DeleteChildren(folderStack.Peek().Id);           
        }

        private void DeleteFolder(object folderId)
        {
            Folder.Delete(Convert.ToInt32(folderId), folderStack.Peek().Id, true);
        }

        private void DeleteFeed(object feedIdString)
        {
            Feed.Delete(Convert.ToInt32(feedIdString), folderStack.Peek().Id, true);
        }

        private void DeleteSelectedMenuItem_Click(object sender, EventArgs e)
        {
            if (isFolderSelected())
                ThreadPool.QueueUserWorkItem(new WaitCallback(DeleteFolder),
                    feedListView.FocusedItem.SubItems[1].Text);
            else
                ThreadPool.QueueUserWorkItem(new WaitCallback(DeleteFeed),
                    feedListView.FocusedItem.SubItems[1].Text);
        }

        private void MainForm_Closing(object sender, CancelEventArgs e)
        {
            // how to shut down worker threads?
            // since download threads take a while, maybe they should be declared as actual threads.
            // this might allow killing them properly when app is closing

            //if (_backgroundThread != null)
            //{
            //    bool shutdownSucceeded = _backgroundThread.Join(_threadShutdownTimeout);
            //    if (!shutdownSucceeded)
            //        _backgroundThread.Abort();
            //}
        }

        private void editFeedMenuItem_Click(object sender, EventArgs e)
        {
            SelectedFolder sf = folderStack.Peek();
            
            Screens.EditFeed.PrepareForm(
                Int32.Parse(feedListView.FocusedItem.SubItems[1].Text)
                , sf.BreadCrumbs
                , this);

            Screens.EditFeed.Show();

            Hide();
        }

        private void exitMenuItem_Click(object sender, EventArgs e)
        {
            Net.StopDownloads();
            this.Close();            
        }

    }
}